/* ///////////////////////////////////////////////////////////////////// ARDUINO/Genuino DUE Sketch for "Wachara 1" https://www.changpuak.ch/electronics/Arduino-Shield-WACHARA1.php As Wachara 1 passed away already, we use a white display "Wachara 1" was the callsign of H.E. Khwankeo Vajarodaya, *03.09.1928 †28.01.2017, my teacher for Siamese Culture and Art Software Version 3.9, 03.09.2019, Alexander Sse Frank, //////////////////////////////////////////////////////////////////////*/ // ///////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////// #include #include // OLED 128x64 with SH1106 Controller // from https://www.displaymodule.com/ // E.G. DM-OLED13-625 #define OLED_MOSI 10 #define OLED_CLK 9 #define OLED_DC 12 #define OLED_CS 13 #define OLED_RESET 11 Adafruit_SH1106 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); #if (SH1106_LCDHEIGHT != 64) #error("Height incorrect, please fix Adafruit_SH1106.h!"); #endif int CURSOR = 2 ; float RX_FREQ = 107.6 ; // Radio Basilisk <3 // float freq = 102.50 ; // Surf 102.5 FM Hua Hin float freq = 94.8 ; // KCS Radio Hua Hin long int pll ; int MAX_BAR_LENGTH = 45 ; int MIN_BAR_LENGTH = 1 ; float MULTI = 2.9 ; // ///////////////////////////////////////////////////// // LM4811 // ///////////////////////////////////////////////////// int VolUpDownPin = A0 ; int VolClkPin = A2 ; int VolShutDownPin = A1 ; int VOLUME = 0 ; int SETVOL = 4 ; int VOLMIN = 0 ; int VOLMAX = 15 ; // 16 STEPS // ///////////////////////////////////////////////////// // TEA 5767 // ///////////////////////////////////////////////////// const int BusMode = A5 ; const int WriteRead = A4 ; const int CursorX = 115 ; const byte TEA5767_ADR = 0x60 ; byte RAM[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} ; float BandStart = 87.5 ; float BandStop = 108.0 ; int RX_CLK_PIN = 21 ; int RX_DAT_PIN = 20 ; // ///////////////////////////////////////////////////// // WRITE // ///////////////////////////////////////////////////// int HLSI = 1 ; // High Side Injection int MUTE = 0 ; // L+R UNMUTED int SM = 0 ; // NOT IN SEARCH MODE int SUD = 1 ; // SEARCH UP int SSL = 1 ; // SEARCH STOP LEVEL = LOW ADC output = 5 int MS = 0 ; // STEREO int MR = 0 ; // RIGHT CHANNEL UNMUTED int ML = 0 ; // LEFT CHANNEL UNMUTED int BL = 0 ; // BAND LIMITS EUROPE int XTAL = 1 ; // 32.768 kHz int SMUTE = 1 ; // SOFT MUTE IS ON int HCC = 0 ; // HIGH CUT CONTROL IS OFF int SNC = 1 ; // STEREO NOISE CANCELLING IS ON int DTC = 0 ; // de-emphasis time constant is 50 µs // ///////////////////////////////////////////////////// // READ // ///////////////////////////////////////////////////// int RF = 0 ; int BLF = 0 ; int Stereo = 0 ; int RSSI = 0 ; int IFCounts = 0 ; volatile boolean SearchMode = false ; // //////////////////////////////////////////////////////////////////// // ROTARY ENCODER // //////////////////////////////////////////////////////////////////// const int KEY1 = 6 ; const int KEY2 = 7 ; const int KEY3 = 5 ; // PRESSED volatile boolean action = false ; volatile boolean pressed = false ; volatile boolean clockwise = false ; volatile boolean rotation = false ; unsigned int ShortLongPressTime = 600 ; // MILLISECONDS // IF THE KNOB WAS PRESSED SHORTER, IT IS CONSIDERED "SHORT" // IF THE KNOB WAS PRESSED LONGER, IT IS CONSIDERED "LONG" boolean LongPress = false ; void ISR_K1 (void) { byte autre = digitalRead(KEY2) ; if (autre == 1) clockwise = true ; else clockwise = false ; action = true ; pressed = false ; rotation = true ; } void ISR_K2 (void) { byte autre = digitalRead(KEY1) ; if (autre == 1) clockwise = true ; else clockwise = false ; action = true ; pressed = false ; rotation = true ; } void ISR_K3 (void) { pressed = true ; action = true ; rotation = false ; } // //////////////////////////////////////////////////////////////////// byte HIBYTE(unsigned int data) { return ((data & 0xFF00) >> 8) ; } // //////////////////////////////////////////////////////////////////// byte LOBYTE(unsigned int data) { return (data & 0x00FF) ; } // //////////////////////////////////////////////////////////////////// void VolumeUp(int HowMuch) { digitalWrite(VolUpDownPin, HIGH); for(int i=0; i < HowMuch; i++) { VOLUME += 1 ; digitalWrite(VolClkPin, HIGH); delay(100) ; digitalWrite(VolClkPin, LOW); delay(100) ; UpdateOLED() ; } } // //////////////////////////////////////////////////////////////////// void VolumeDown(int HowMuch) { digitalWrite(VolUpDownPin, LOW); for(int i=0; i < HowMuch; i++) { digitalWrite(VolClkPin, HIGH); digitalWrite(VolClkPin, LOW); } VOLUME = 0 ; UpdateOLED() ; } // //////////////////////////////////////////////////////////////////// void SET_VOLUME() { digitalWrite(VolUpDownPin, LOW); for(int i=0; i < 18; i++) { digitalWrite(VolClkPin, HIGH); digitalWrite(VolClkPin, LOW); } digitalWrite(VolUpDownPin, HIGH); for(int i=0; i < VOLUME; i++) { digitalWrite(VolClkPin, HIGH); digitalWrite(VolClkPin, LOW); } UpdateOLED() ; } // //////////////////////////////////////////////////////////////////// void VolumeMute() { digitalWrite(VolShutDownPin, HIGH); } // //////////////////////////////////////////////////////////////////// void VolumeUnMute() { digitalWrite(VolShutDownPin, LOW); } // //////////////////////////////////////////////////////////////////// void setup() { // TEA5767 pinMode(BusMode, OUTPUT); digitalWrite(BusMode, HIGH); pinMode(RX_DAT_PIN, OUTPUT); digitalWrite(RX_DAT_PIN, LOW); pinMode(RX_CLK_PIN, OUTPUT); digitalWrite(RX_CLK_PIN, LOW); pinMode(WriteRead, OUTPUT); digitalWrite(WriteRead, HIGH); // LMX4811 pinMode(VolUpDownPin, OUTPUT); digitalWrite(VolUpDownPin, LOW); // JUST TO HAVE A DEFINED LEVEL pinMode(VolClkPin, OUTPUT); digitalWrite(VolClkPin, LOW); pinMode(VolShutDownPin, OUTPUT); digitalWrite(VolShutDownPin, LOW); // SWITCH THAT THING ON // This shutdown function is activated by applying a logic high // to the SHUTDOWN pin. Serial.begin(9600); // SERIAL // INIT OLED display.begin(SH1106_SWITCHCAPVCC); // SHOW STARTUP SCREEN display.clearDisplay(); display.drawLine(0, 14, 128, 14, WHITE); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.print("*****") ; display.setCursor(33,0); display.print(" MIRCEMK") ; display.setCursor(90,0); display.print("******") ; display.setTextSize(1); display.setCursor(0,21); display.println("A MULTIBAND RECEIVER"); display.setCursor(0,33); display.println("WITH NXP's TEA5767"); display.setCursor(0,45); display.println("IN MEMORIAM WACHARA 1"); display.setCursor(0,57); display.println("BUILT 03.09.2019"); display.display(); delay(4999) ; VolumeDown(18) ; // YES, WE USE THE COMMODORE 1541 APPROACH HERE // FREQ_TEA5767(freq) ; HiLoOptimiser() ; delay(100) ; // FREQ_TEA5767(freq) ; HiLoOptimiser() ; Serial.println("TEA5767 RADIO, TYPE WACHARA 1"); Serial.println("-----------------------------"); READ_TEA5767() ; Serial.println(RAM[0],HEX) ; Serial.println(RAM[1],HEX) ; Serial.println(RAM[2],HEX) ; Serial.println(RAM[3],HEX) ; Serial.println(RAM[4],HEX) ; Serial.println("-----------------------------"); VOLUME = SETVOL ; SET_VOLUME() ; UpdateOLED() ; // ROTARY ENCODER pinMode(KEY1, INPUT_PULLUP) ; pinMode(KEY2, INPUT_PULLUP) ; pinMode(KEY3, INPUT_PULLUP) ; // INTERRUPTS ROTARY ENCODER attachInterrupt(digitalPinToInterrupt(KEY1), ISR_K1, RISING); // ROT attachInterrupt(digitalPinToInterrupt(KEY2), ISR_K2, FALLING); // ROT attachInterrupt(digitalPinToInterrupt(KEY3), ISR_K3, FALLING); // PRESS delay(3999); action = false ; pressed = false ; clockwise = false ; rotation = false ; // SCAN() ; freq = 94.80 ; HiLoOptimiser() ; } // //////////////////////////////////////////////////////////////////// void BAR(int x, int y, int value) { if (value > MAX_BAR_LENGTH) value = MAX_BAR_LENGTH ; if (value < MIN_BAR_LENGTH) value = MIN_BAR_LENGTH ; display.drawRect(x, y, MAX_BAR_LENGTH, 7, WHITE); display.fillRect(x, y, value, 7, WHITE); } // //////////////////////////////////////////////////////////////////// void UpdateOLED() { display.clearDisplay(); display.drawLine(0, 11, 128, 11, WHITE); display.drawLine(0, 32, 128, 32, WHITE); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.print("*****") ; display.setCursor(33,0); display.print(" MIRCEMK") ; display.setCursor(90,0); display.print("******") ; display.setTextSize(2); display.setCursor(0,15); if(!SearchMode) { if (freq < 100.0) display.print(" "); if (freq < 10.0) display.print(" "); display.print(freq,3); // display.print("0"); // CURSOR if (CURSOR == 0) { display.setCursor( 0,19); display.print("_") ; } if (CURSOR == 1) { display.setCursor(12,19); display.print("_") ; } if (CURSOR == 2) { display.setCursor(24,19); display.print("_") ; } if (CURSOR == 3) { display.setCursor(48,19); display.print("_") ; } if (CURSOR == 4) { display.setCursor(60,19); display.print("_") ; } display.setCursor(92,15); display.print("MHz"); } if(SearchMode) { display.setCursor(10,15); display.print("SEARCHING"); } display.setTextSize(1); display.setCursor(0,37); display.print("RSSI "); if(HLSI == 1) display.print("+"); if(HLSI == 0) display.print("-"); BAR(40, 37, (RSSI*3) ) ; display.setCursor(91,37); if (Stereo == 1) display.print("STEREO"); if (Stereo == 0) display.print(" MONO"); display.setCursor(0,47); display.print("VOLUME "); BAR(40, 47, (VOLUME*3) ) ; if (CURSOR == 5) { display.setCursor(91,47); display.print("<<<") ; } display.setCursor(0,57); display.print("PRESS LONG TO SEARCH"); display.display(); } void ButtonPressed() { // ////////////////////////////////////////////////// // WAS IT PRESSED LONG >>> START SEARCH UP // ////////////////////////////////////////////////// LongPress = false ; delay(ShortLongPressTime) ; if(digitalRead(KEY3) == 0) LongPress = true ; if(LongPress) { // ////////////////////////////////////////////////// // SEARCH UPWARDS // ////////////////////////////////////////////////// Serial.println("PRESSED LONG") ; SearchUP() ; pressed = false ; } // ////////////////////////////////////////////////// // WAS IT PRESSED SHORT >>> MOVE CURSOR RIGHT // ////////////////////////////////////////////////// if(!LongPress) { Serial.println("PRESSED SHORT") ; CURSOR += 1 ; if(CURSOR > 5) CURSOR = 2 ; } pressed = false ; } void ButtonRotated() { // ////////////////////////////////////////////////// // WAS IT ROTATED RIGHT >>> INCREASE // ////////////////////////////////////////////////// if(clockwise) { // Serial.println("CW") ; switch(CURSOR) { case 2: freq = freq - 1.0 ; if(freq < BandStart) freq = BandStart ; HiLoOptimiser() ; delay(10) ; READ_TEA5767() ; // RSSI break ; case 3: freq = freq - 0.1 ; if(freq < BandStart) freq = BandStart ; HiLoOptimiser() ; delay(10) ; READ_TEA5767() ; // RSSI break ; case 4: freq = freq - 0.01 ; if(freq < BandStart) freq = BandStart ; HiLoOptimiser() ; delay(10) ; READ_TEA5767() ; // RSSI break ; case 5: VOLUME = VOLUME - 1 ; if(VOLUME < VOLMIN ) VOLUME = VOLMIN ; SET_VOLUME() ; break ; } rotation = false ; UpdateOLED() ; } // ////////////////////////////////////////////////// // WAS IT ROTATED LEFT >>> DECREASE // ////////////////////////////////////////////////// if(!clockwise) { // Serial.println("CCW") ; switch(CURSOR) { case 2: freq = freq + 1.0 ; if(freq > BandStop) freq = BandStop ; HiLoOptimiser() ; delay(10) ; READ_TEA5767() ; // RSSI break ; case 3: freq = freq + 0.1 ; if(freq > BandStop) freq = BandStop ; HiLoOptimiser() ; delay(10) ; READ_TEA5767() ; // RSSI break ; case 4: freq = freq + 0.01 ; if(freq > BandStop) freq = BandStop ; HiLoOptimiser() ; delay(10) ; READ_TEA5767() ; // RSSI break ; case 5: VOLUME = VOLUME + 1 ; if(VOLUME > VOLMAX ) VOLUME = VOLMAX ; SET_VOLUME() ; break ; } rotation = false ; UpdateOLED() ; } } void READ_TEA5767() { pinMode(RX_DAT_PIN, INPUT); digitalWrite(RX_CLK_PIN, HIGH); delay(1) ; digitalWrite(WriteRead, HIGH); delay(1) ; digitalWrite(WriteRead, LOW); delay(1) ; RAM[0] = shiftIn(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST) ; RAM[1] = shiftIn(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST) ; RAM[2] = shiftIn(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST) ; RAM[3] = shiftIn(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST) ; RAM[4] = shiftIn(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST) ; Stereo = (RAM[2] & 0x80) >> 7 ; RSSI = (RAM[3] & 0xF0) >> 4 ; RF = (RAM[0] & 0x80) >> 7 ; BLF = (RAM[0] & 0x40) >> 6 ; IFCounts = RAM[2] & 0x7F ; } void WRITE_TEA5767() { pinMode(RX_DAT_PIN, OUTPUT); digitalWrite(RX_CLK_PIN, LOW); delay(1) ; digitalWrite(WriteRead, LOW); delay(1) ; digitalWrite(WriteRead, HIGH); delay(1) ; if(HLSI == 1) pll=(int)(((freq+0.225)*122.0703125)+0.5); if(HLSI == 0) pll=(int)(((freq-0.225)*122.0703125)+0.5); RAM[5] = HIBYTE(pll) | (MUTE<<7) | (SM<<6) ; RAM[6] = LOBYTE(pll) ; RAM[7] = (SUD<<7)|(SSL<<5)|(HLSI<<4)|(MS<<3)|(MR<<2)|(ML<<1) ; RAM[8] = (BL<<5)|(XTAL<<4)|(SMUTE<<3)|(HCC<<2)|(SNC<<1); RAM[9] = DTC<<6 ; shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[5]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[6]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[7]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[8]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[9]); } void HiLoOptimiser() { int RSSIHigh = 0 ; int RSSILow = 0 ; float Frequency = freq ; SM = 0 ; // NOT IN SEARCH MODE VolumeMute() ; if (Frequency > BandStop) Frequency = BandStop ; if (Frequency < BandStart) Frequency = BandStart ; // ////////////////////////////////////////////////// // RSSIHigh is at freq + 450 kHz // ////////////////////////////////////////////////// freq = Frequency + 0.225 ; HLSI = 1 ; WRITE_TEA5767() ; delay(10) ; READ_TEA5767() ; RSSIHigh = RSSI ; // ////////////////////////////////////////////////// // RSSILow is at freq - 450 kHz // ////////////////////////////////////////////////// HLSI = 0 ; freq = Frequency - 0.225 ; WRITE_TEA5767() ; delay(10) ; READ_TEA5767() ; RSSILow = RSSI ; // ////////////////////////////////////////////////// // EVALUATION SEE APPLICATION NOTE PAGE 27 // ////////////////////////////////////////////////// if(RSSIHigh < RSSILow) { // OPTIMUM SETTING IS HIGH SIDE INJECTION HLSI = 1 ; freq = Frequency ; WRITE_TEA5767() ; } else { // OPTIMUM SETTING IS LOW SIDE INJECTION HLSI = 0 ; freq = Frequency ; WRITE_TEA5767() ; } delay(27); READ_TEA5767() ; VolumeUnMute() ; UpdateOLED() ; } void SCAN() { freq = BandStart ; while(freq <= BandStop) { HiLoOptimiser() ; if(RSSI >=4) { if(freq <100.0) Serial.print(" "); Serial.print(freq,3); Serial.print(" RSSI: "); Serial.print(RSSI,DEC); Serial.print(" IFC: "); Serial.println(IFCounts,DEC); } freq += 0.098304 ; delay(30); } } void SearchUP() { Serial.println("SEARCHING ..."); SearchMode = true ; UpdateOLED() ; int RSSILAST = 0 ; int RSSICURR = 0 ; int RSSINEXT = 0 ; boolean STOPSEARCH = false ; float Frequency = freq ; while(!STOPSEARCH) { freq += 0.098304 ; HiLoOptimiser() ; if(RSSI >=4) { if(freq <100.0) Serial.print(" "); Serial.print(freq,3); Serial.print(" RSSI: "); if(RSSI < 10) Serial.print(" "); Serial.print(RSSI,DEC); Serial.print(" IFC: "); Serial.println(IFCounts,DEC); if((IFCounts > 56) && (IFCounts < 71)) { STOPSEARCH = true ; SearchMode = false ; Serial.println("FOUND."); } } // END RSSI-IF if(freq >= BandStop) { freq = BandStart ; STOPSEARCH = true ; SearchMode = false ; } // END BANDSTOP delay(100); } // END WHILE SearchMode = false ; UpdateOLED() ; } // ///////////////////////////////////////////////////////////// // UNUSED FUNCTIONS // ///////////////////////////////////////////////////////////// void FREQ_TEA5767(float FRQ) { int aux ; if (FRQ > BandStop) FRQ = BandStop ; if (FRQ < BandStart) FRQ = BandStart ; pll = (int)((( FRQ + 0.225 ) * 122.0703125 ) + 0.5 ); RAM[5] = HIBYTE(pll) ; RAM[6] = LOBYTE(pll) ; // Search Up, Low Volume, High Side Injection, Stereo, No Mute RAM[7] = 0xC0; // EU Band, Clock 32768, Stero Noise Cancelling On RAM[8] = 0x12 ; RAM[9] = 0x00 ; // De-Emphasis Time Constant is 50 µs pinMode(RX_DAT_PIN, OUTPUT); delay(1) ; digitalWrite(WriteRead, LOW); delay(1) ; digitalWrite(WriteRead, HIGH); delay(1) ; shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[5]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[6]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[7]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[8]); shiftOut(RX_DAT_PIN, RX_CLK_PIN, MSBFIRST, RAM[9]); } // ///////////////////////////////////////////////////////////// void loop() { if (action) { if(pressed) ButtonPressed() ; if(rotation) ButtonRotated() ; action = false ; UpdateOLED() ; } delay(9) ; } // ///////////////////////////////////////////////////////////// // END OF FILE // /////////////////////////////////////////////////////////////